package com.example.demo.common.r2dbc;

import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.repository.support.SimpleR2dbcRepository;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.relational.repository.query.RelationalExampleMapper;
import org.springframework.data.util.Lazy;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Transactional(readOnly = true)
public class BaseR2dbcRepository<T, ID> extends SimpleR2dbcRepository<T, ID> implements BaseRepository<T, ID> {

    private final RelationalEntityInformation<T, ID> entity;
    private final R2dbcEntityOperations entityOperations;
    private final Lazy<RelationalPersistentProperty> idProperty;
    private final RelationalExampleMapper exampleMapper;

    public BaseR2dbcRepository(RelationalEntityInformation<T, ID> entity, R2dbcEntityOperations entityOperations, R2dbcConverter converter) {
        super(entity, entityOperations, converter);
        this.entity = entity;
        this.entityOperations = entityOperations;
        this.idProperty = Lazy.of(() -> converter.getMappingContext().getRequiredPersistentEntity(this.entity.getJavaType()).getRequiredIdProperty());
        this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext());
    }

    @Override
    public Mono<T> findOneWithQuery(Query query) {
        Assert.notNull(query, "Query must not be null");
        return this.entityOperations.selectOne(query, this.entity.getJavaType());
    }

    @Override
    public Flux<T> findListWithQuery(Query query) {
        Assert.notNull(query, "Query must not be null");
        return this.entityOperations.select(query, this.entity.getJavaType());
    }

    @Override
    public Flux<T> findListWithPage(Pageable pageable) {
        Assert.notNull(pageable, "Pageable must not be null");
        return this.entityOperations.select(Query.empty().with(pageable), this.entity.getJavaType());
    }

    @Override
    public Flux<T> findListWithQueryAndPage(Query query, Pageable pageable) {
        Assert.notNull(query, "Query must not be null");
        Assert.notNull(pageable, "Pageable must not be null");
        return this.entityOperations.select(query.with(pageable), this.entity.getJavaType());
    }
}
